﻿
using Modbus.Device;
using Modbus.Utility;
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Documents;
using System.Windows.Media;
using ZDK.Infra.Constants;
using ZDK.Infra.Helpers;
using ZDK.Infra.Services;
using ZDK.Utilities;
using TcpClient = System.Net.Sockets.TcpClient;

namespace ZDK.ViewModels
{
    public class UpgradeViewModel : BindableBase
    {

        readonly IMyLog _log;

        #region 属性
        private UpgradeTransModelEnum transModel = UpgradeTransModelEnum.RTU;
        public UpgradeTransModelEnum TransModel { get => transModel; set => SetProperty(ref transModel, value); }
        private string byClient_ServerIP = "192.168.1.88";
        public string ByClient_ServerIP { get => byClient_ServerIP; set => SetProperty(ref byClient_ServerIP, value); }
        private int byClient_ServerPort = 502;
        public int ByClient_ServerPort { get => byClient_ServerPort; set => SetProperty(ref byClient_ServerPort, value); }
        private int byClient_SlaveID = 1;
        public int ByClient_SlaveID { get => byClient_SlaveID; set => SetProperty(ref byClient_SlaveID, value); }
        private string byClient_StartAddr = "0x5000";
        public string ByClient_StartAddr { get => byClient_StartAddr; set => SetProperty(ref byClient_StartAddr, value); }
        private int byClient_Timeout = 5000;
        public int ByClient_Timeout { get => byClient_Timeout; set => SetProperty(ref byClient_Timeout, value); }
        private string byClient_FilePath = "";
        public string ByClient_FilePath { get => byClient_FilePath; set => SetProperty(ref byClient_FilePath, value); }
        private bool byClientSendFileBtnEnabled = true;
        public bool ByClientSendFileBtnEnabled { get => byClientSendFileBtnEnabled; set => SetProperty(ref byClientSendFileBtnEnabled, value); }
        #endregion


        public UpgradeViewModel(IMyLog log)
        {
            _log = log;
        }

        private DelegateCommand byClient_ChooseFileCmd;
        public DelegateCommand ByClientChooseFileCmd => byClient_ChooseFileCmd ?? (byClient_ChooseFileCmd = new DelegateCommand(doByClientChooseFile));
        void doByClientChooseFile() 
        {
            var dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.RestoreDirectory = true;//记忆
            dlg.DefaultExt = "bin";
            var result = dlg.ShowDialog();
            if(result == true)
            {
                ByClient_FilePath = dlg.FileName;
            }
        }

        private DelegateCommand byClientSendFileCmd;
        public DelegateCommand ByClientSendFileCmd => byClientSendFileCmd ?? (byClientSendFileCmd = new DelegateCommand(doByClientSendFile));
        async void doByClientSendFile() 
        {
            if (string.IsNullOrEmpty(ByClient_FilePath))
            {
                _log.Error("请先选择要发送的文件！");
                return;
            }
            ByClientSendFileBtnEnabled = false;

            //建立socket连接，构造发送报文，处理异常
            _log.Info("开始读取文件");
            /* 同步socket模式
            try
            {
                FileStream fs = new FileStream(ByClient_FilePath, FileMode.Open);
                //计算文件长度，补足4的倍数
                int fileLen = (int)fs.Length;
                if (fileLen % 4 != 0)
                {
                    fileLen += 4 - (fileLen % 4);
                }
                byte[] fileBuff = new byte[fileLen];
                fs.Read(fileBuff, 0, fileLen);
                _log.Info($"开始发送文件，文件大小:{fs.Length}，补齐:{fileLen}字节");
                fs.Close();
                fs.Dispose();

                int pkgSize = 200; //一帧200字节
                int pkgCount = fileLen / pkgSize;
                if ((fileLen % pkgSize) != 0)
                {
                    pkgCount++;
                }
                var startAddr = StrHelper.StrToHexByte(ByClient_StartAddr);
                var skt = new SyncSocket(_log, ByClient_ServerIP, ByClient_ServerPort, ByClient_Timeout);
                if(!skt.Connect())
                {
                    _log.Info("连接服务端失败！");
                    return;
                }
                _log.Info("连接服务端成功");

                //下载请求包
                var msg = new List<byte>
                    {
                        (byte)ByClient_SlaveID,
                        0x10, startAddr[0], startAddr[1], 0x00, 0x06, 0x0C,
                        0x00, //升级对象地址
                        0x01, //包属性，下载请求
                        0x00, 0x00, //软件新版本
                        (byte)(pkgCount >> 8), (byte)(pkgCount & 0xFF), //文件分包数量
                        (byte)(fileLen>>24), (byte)(fileLen>>16), //文件长度
                        (byte)(fileLen>>8), (byte)(fileLen&0xFF)
                    };
                var crc = ModbusUtility.CalculateCrc(fileBuff);
                msg.Add(crc[0]); //文件crc
                msg.Add(crc[1]);
                crc = ModbusUtility.CalculateCrc(msg.ToArray());
                msg.Add(crc[0]); //报文crc
                msg.Add(crc[1]);
                _log.Info($"发送下载请求包:{BitConverter.ToString(msg.ToArray())}");
                var respPkg = skt.WriteSyncData(msg.ToArray());
                if(respPkg == null)
                {
                    _log.Error("下载请求包返回异常！"); return;
                }
                _log.Info($"下载请求包返回报文:{BitConverter.ToString(respPkg)}");

                //程序数据包
                //ushort SEQ = 0; //包序号
                int retryTimes = 0; //重发次数
                int lastPkgSeq = 0; //上一个包序号
                for (int i = 0; i < pkgCount; ++i)
                {
                    if (retryTimes == 10)
                    {
                        _log.Warn($"已重复{retryTimes}次！下载失败！"); return;
                    }
                    //SEQ++;
                    int dtLen = pkgSize;
                    if (i == pkgCount - 1)
                    {
                        int len = fileLen % pkgSize; //最后一帧长度
                        if (len == 0) len = pkgSize;
                        dtLen = len;
                    }
                    byte[] dt = new byte[dtLen];
                    Buffer.BlockCopy(fileBuff, i * pkgSize, dt, 0, pkgSize);
                    byte[] btSEQ = BitConverter.GetBytes(i + 1); //包序号从1开始
                    int regNum = (dtLen + 4) / 2;
                    msg = new List<byte>
                        {
                            (byte)ByClient_SlaveID,
                            0x10, startAddr[0], startAddr[1],
                            (byte)(regNum>>8), (byte)(regNum&0xFF), (byte)(dtLen+4), //包长度
                            0x00, //升级对象地址
                            0x00, //包属性，程序数据
                            btSEQ[1], btSEQ[0]
                        };
                    var pkg = new byte[msg.Count + dtLen];
                    Buffer.BlockCopy(msg.ToArray(), 0, pkg, 0, msg.Count);
                    Buffer.BlockCopy(fileBuff, i * pkgSize, pkg, msg.Count, pkgSize);
                    crc = ModbusUtility.CalculateCrc(pkg);
                    var sendPkg = new byte[pkg.Length + 2];
                    Buffer.BlockCopy(pkg, 0, sendPkg, 0, pkg.Length);
                    sendPkg[sendPkg.Length - 2] = crc[0];
                    sendPkg[sendPkg.Length - 1] = crc[1];
                    _log.Info($"发送程序数据包【{i + 1}】:{BitConverter.ToString(sendPkg)}");
                    respPkg = skt.WriteSyncData(sendPkg);
                    if(respPkg == null)
                    {
                        _log.Error("程序数据包返回异常！");
                        //重发上一个包
                        if (i != lastPkgSeq)
                        {
                            retryTimes = 1;
                            lastPkgSeq = i--;
                        }
                        else
                        {
                            retryTimes++;
                        }
                    }
                    else
                    {
                        _log.Info($"程序数据包返回报文:{BitConverter.ToString(respPkg)}");
                        lastPkgSeq = i;
                    }
                }
                _log.Info("发送完成！");
            }
            catch (Exception ex)
            {
                _log.Error($"软件升级异常:{ex.Message}");
            }
            */
            
            try
            {
                FileStream fs = new FileStream(ByClient_FilePath, FileMode.Open);
                //计算文件长度，补足4的倍数
                int fileLen = (int)fs.Length;
                if (fileLen % 4 != 0)
                {
                    fileLen += 4 - (fileLen % 4);
                }
                byte[] fileBuff = new byte[fileLen];
                fs.Read(fileBuff, 0, fileLen);
                _log.Info($"开始发送文件，文件大小:{fs.Length}，补齐:{fileLen}字节");
                fs.Close();
                fs.Dispose();
                
                int pkgSize = 200; //一帧200字节
                int pkgCount = fileLen / pkgSize;
                if ((fileLen % pkgSize) != 0)
                {
                    pkgCount++;
                }
                var startAddr = StrHelper.StrToHexByte(ByClient_StartAddr);
                using (TcpClient client = new TcpClient())
                {
                    client.ReceiveTimeout = ByClient_Timeout;
                    await client.ConnectAsync(ByClient_ServerIP, ByClient_ServerPort);
                    NetworkStream stream = client.GetStream();
                    _log.Info("连接服务端成功");

                    //下载请求包
                    var msg = new List<byte>
                    {
                        (byte)ByClient_SlaveID,
                        0x10, startAddr[0], startAddr[1], 0x00, 0x06, 0x0C,
                        0x00, //升级对象地址
                        0x01, //包属性，下载请求
                        0x00, 0x00, //软件新版本
                        (byte)(pkgCount >> 8), (byte)(pkgCount & 0xFF), //文件分包数量
                        (byte)(fileLen>>24), (byte)(fileLen>>16), //文件长度
                        (byte)(fileLen>>8), (byte)(fileLen&0xFF)
                    };
                    var crc = ModbusUtility.CalculateCrc(fileBuff);
                    msg.Add(crc[0]); //文件crc
                    msg.Add(crc[1]);
                    crc = ModbusUtility.CalculateCrc(msg.ToArray());
                    msg.Add(crc[0]); //报文crc
                    msg.Add(crc[1]);
                    _log.Info($"发送下载请求包:{BitConverter.ToString(msg.ToArray())}");
                    await stream.WriteAsync(msg.ToArray(), 0, msg.Count);
                    var respBuf = new byte[256];
                    //超时任务（CancellationTokenSource无效）
                    //var timeoutTask = Task.Delay(ByClient_Timeout);
                    using (var cts = new CancellationTokenSource(ByClient_Timeout))
                    {
                        //cts.CancelAfter(ByClient_Timeout);
                        //var completedTask = await Task.WhenAny(timeoutTask);
                        int nRead = await stream.ReadAsync(respBuf, 0, respBuf.Length, cts.Token);
                        var resp = new byte[nRead];
                        Array.Copy(respBuf, resp, nRead);
                        _log.Info($"接收报文:{BitConverter.ToString(resp)}");
                        //异常判断 0x10 + 0x80 代表异常
                        if(resp.Length > 4 && resp[1] != 0x10)
                        {
                            _log.Error("下载请求包返回异常！"); return;
                        } 
                    }
                    //程序数据包
                    //await Task.Delay(5000); //等待5秒后发送
                    //ushort SEQ = 0; //包序号
                    int retryTimes = 0; //重发次数
                    int lastPkgSeq = 0; //上一个包序号
                    for (int i = 0; i < pkgCount; ++i)
                    {
                        if(retryTimes == 10)
                        {
                            _log.Warn($"已重复{retryTimes}次！下载失败！"); return;
                        }
                        //SEQ++;
                        int dtLen = pkgSize;
                        if (i == pkgCount - 1)
                        {
                            int len = fileLen % pkgSize; //最后一帧长度
                            if (len == 0) len = pkgSize;
                            dtLen = len;
                        }
                        byte[] dt = new byte[dtLen];
                        Buffer.BlockCopy(fileBuff, i * pkgSize, dt, 0, dtLen);
                        byte[] btSEQ = BitConverter.GetBytes(i+1); //包序号从1开始
                        int regNum = (dtLen+4) / 2;
                        msg = new List<byte>
                        {
                            (byte)ByClient_SlaveID,
                            0x10, startAddr[0], startAddr[1],
                            (byte)(regNum>>8), (byte)(regNum&0xFF), (byte)(dtLen+4), //包长度
                            0x00, //升级对象地址
                            0x00, //包属性，程序数据
                            btSEQ[1], btSEQ[0] 
                        };
                        var pkg = new byte[msg.Count + dtLen];
                        Buffer.BlockCopy(msg.ToArray(), 0, pkg, 0, msg.Count);
                        Buffer.BlockCopy(fileBuff, i * pkgSize, pkg, msg.Count, dtLen);
                        crc = ModbusUtility.CalculateCrc(pkg);
                        var sendPkg = new byte[pkg.Length + 2];
                        Buffer.BlockCopy(pkg, 0, sendPkg, 0, pkg.Length);
                        sendPkg[sendPkg.Length - 2] = crc[0];
                        sendPkg[sendPkg.Length - 1] = crc[1];
                        _log.Info($"发送程序数据包【{i+1}】:{BitConverter.ToString(sendPkg)}");
                        await stream.WriteAsync(sendPkg, 0, sendPkg.Length);
                        respBuf = new byte[256];
                        using (var cts2 = new CancellationTokenSource(ByClient_Timeout))
                        {
                            int nRead = await stream.ReadAsync(respBuf, 0, respBuf.Length, cts2.Token);
                            var resp = new byte[nRead];
                            Array.Copy(respBuf, resp, nRead);
                            _log.Info($"接收报文:{BitConverter.ToString(resp)}");
                            //异常判断 0x10 + 0x80 代表异常
                            if (resp.Length > 4 && resp[1] != 0x10)
                            {
                                _log.Error("程序数据包返回异常！");
                                //重发上一个包
                                if (i != lastPkgSeq)
                                {
                                    retryTimes = 1;
                                    lastPkgSeq = i--;
                                }
                                else
                                {
                                    retryTimes++;
                                }
                                await Task.Delay(250);
                            }
                        }
                        //await Task.Delay(250); //每个包等待
                    }
                    _log.Info("发送完成！");
                } 
            }
            catch (OperationCanceledException timeoutEx)
            {
                _log.Error($"软件升级超时:{timeoutEx.Message}");
            }
            catch (Exception ex)
            {
                _log.Error($"软件升级异常:{ex.Message}");
            }
            finally
            {
                ByClientSendFileBtnEnabled = true;
            }
        }
        //async Task<int> 
        //async Task<int> ExecByClient_SendPkgWithTimeoutAsync()
        //{
        //    Task timeoutTask = Task.Delay(ByClient_Timeout);
        //    Task completedTask = Task.WhenAny(timeoutTask, );
        //}
    }
}
